package patternmining.model.construction;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** Class that permits centralized lookup of members of any type defined in the java project analyzed
 *  or in the dependency types.
 * 
 *  Attention: The lookup does not verify that a queried member of some type is in the scope (is visible)
 *  of the source place  where the lookup is perforemd. Access modifier issues are completely ignored.
 *  The validy of the lookup has to be assured by the source processing sequence. As a first step there
 *  shall be performed a javac compilation. By definition the following analysis and an eventual lookup of members
 *  is done only for valid java source code, that passes the validy analysis of javac.
 * 
 * Is a singelton to reflect the centralized lookup concept
 * 
 * @version 1.0, 15.07.2008
 * @author Holger Wiehen
 */
class Project {

    
    private Map<String, MemberTable> projectTypes; 
    private ProjectDependencies dependencyData;
    
    private static Project uniqueInstance = null;
    
    private Project(){
        projectTypes = new HashMap<String, MemberTable>();
        dependencyData = ProjectDependencies.getInstance();
    }
    
    /**Method constitutes the only way to obtain a instance of this class.
     * 
     * @return the singleton instance of this class
     */
    static Project getInstance(){
        if( uniqueInstance == null )  uniqueInstance = new Project();
        return uniqueInstance;
    }
    
    /**Register a type and its collected members at this central, project wide ressource lookup
     * 
     * @param ownerType the type to register
     * @param members its members, earlier extracted from the source
     * @throws patternmining.model.construction.TypeCollissionException
     */
    void addType(String ownerType, MemberTable members)throws TypeCollissionException{
        if( projectTypes.containsKey(ownerType) )    throw new TypeCollissionException();
        projectTypes.put(ownerType, members);
    }
    
    /**Returns the type of a attribute that is defined in some type
     * 
     * @param ownerType the type in which the attribute is defined
     * @param attributeName the name of the queried attribute
     * @return the solicitated type of the attribute
     * @throws patternmining.model.construction.UnknownTypeException
     * @throws patternmining.model.construction.UnknownMemberException
     */
    String getAttributeType(String ownerType, String attributeName) throws UnknownTypeException, UnknownMemberException{
        if( ! projectTypes.containsKey(ownerType) && ! dependencyData.containsType(ownerType) )     throw new UnknownTypeException();
        // if type is not allready in the list, pull the member data from the project dependencies and store it now in the list
        // permits to return to the caller the requested data and following requests to types of this member can be answered with less processing
        // (lazy strategy of list completition with the member data of types that are added through dependencies)
        if( ! projectTypes.containsKey(ownerType) ) projectTypes.put(ownerType, dependencyData.getMembersOfType( ownerType ) );
        return projectTypes.get(ownerType).getAttributeType(attributeName);
    }
    
    /**Returns the return type of a method defiened in some type
     * 
     * @param ownerType the type in which the method is defined
     * @param methodName the name of the queried method
     * @param parametersTypes the parameter type sequence of the queried method ( to identify overloaded methods uniquely )
     * @return
     * @throws patternmining.model.construction.UnknownTypeException
     * @throws patternmining.model.construction.UnknownMemberException
     */
    String getMethodReturnType(String ownerType, String methodName, List<String> parametersTypes) throws UnknownTypeException, UnknownMemberException{
        if( ! projectTypes.containsKey(ownerType) && ! dependencyData.containsType(ownerType) )     throw new UnknownTypeException();
        // if type is not allready in the list, pull the member data from the project dependencies and store it now in the list
        // permits to return to the caller the requested data and following requests to types of this member can be answered with less processing
        // (lazy strategy of list completition with the member data of types that are added through dependencies)
        if( ! projectTypes.containsKey(ownerType) ) projectTypes.put(ownerType, dependencyData.getMembersOfType( ownerType ) );
        return projectTypes.get(ownerType).getMethodReturnType(methodName, parametersTypes);
    }
    
    /**Returns the unique name of a method defiened in some type
     * For every method who's definition is extracted from the sources is created a unique name, to reference
     * destinguishly to overloaded methods.
     * 
     * @param ownerType the type in which the method is defined
     * @param methodName the name of the queried method
     * @param parametersTypes the parameter type sequence of the queried method ( to identify overloaded methods uniquely )
     * @return
     * @throws patternmining.model.construction.UnknownTypeException
     * @throws patternmining.model.construction.UnknownMemberException
     */
    String getUniqueMethodName(String ownerType, String methodName, List<String> parametersTypes) throws UnknownTypeException, UnknownMemberException{
        if( ! projectTypes.containsKey(ownerType) && ! dependencyData.containsType(ownerType) )     throw new UnknownTypeException();
        // if type is not allready in the list, pull the member data from the project dependencies and store it now in the list
        // permits to return to the caller the requested data and following requests to types of this member can be answered with less processing
        // (lazy strategy of list completition with the member data of types that are added through dependencies)
        if( ! projectTypes.containsKey(ownerType) ) projectTypes.put(ownerType, dependencyData.getMembersOfType( ownerType ) );
        return projectTypes.get(ownerType).getUniqueMethodName(methodName, parametersTypes);
    }   
}

class TypeCollissionException extends Exception{
    
}

class UnknownTypeException extends Exception{
    
}
